package edu.bu.CS893;

import java.util.HashMap;
import java.util.Map;

import edu.bu.CS893.data.FacebookApiCredentials;
import edu.bu.CS893.data.FacebookApiCredentialsReader;
import edu.bu.CS893.data.FacebookApiFetcher;
import edu.bu.CS893.data.FacebookData;
import edu.bu.CS893.data.FacebookSession;
import edu.bu.CS893.data.FacebookUser;

import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.DeadObjectException;
import android.os.IBinder;

/** 
 * This is the main service that connects to facebook 
 * and retrieves information  
 */

public class FacebookService extends Service {
	private static String METHOD_CREATE_TOKEN = "auth.createToken"; 
	private static String METHOD_GET_SESSION = "auth.getSession";
	private static String METHOD_FQL = "fql.query";
	private static String PREFS_NAME = "fbprefs";
	private FacebookApiCredentials _fb_api_credentials;
	private String _fb_auth_token = null;
	private FacebookSession _fb_session = null;
	private Map<String, FacebookUser> _names_to_fb_users;
	private Map<Long, FacebookUser> _uids_to_fb_users;

    /**
     * The IRemoteInterface is defined through IDL
     */
    private final IFacebookService.Stub _binder = new IFacebookService.Stub() {
    	
		@Override
		public String getNameFromUid(long theUid) throws DeadObjectException {
			FacebookUser aUser = getUserFromUID(theUid);
			return (aUser != null) ? aUser.name : null;
		}

		@Override
		public String getPicFromUid(long theUid) throws DeadObjectException {
			FacebookUser aUser = getUserFromUID(theUid);
			return (aUser != null) ? aUser.pic : null;
		}
		
		@Override
		public String getStatusFromUid(long theUid) throws DeadObjectException {
			FacebookUser aUser = getUserFromUID(theUid);
			return (aUser != null) ? aUser.status : null;
		}

		@Override
		public long getUidFromName(String theName) throws DeadObjectException {
			FacebookUser aUser = getUserFromName(theName);
			return (aUser != null) ? aUser.uid : -1;
		}
		
		@Override
		public String getPicFromName(String theName) throws DeadObjectException {
			FacebookUser aUser = getUserFromName(theName);
			return (aUser != null) ? aUser.pic : null;
		}
		
		@Override
		public String getStatusFromName(String theName) throws DeadObjectException {
			FacebookUser aUser = getUserFromName(theName);
			return (aUser != null) ? aUser.status : null;
		}
		
    	private FacebookUser getUserFromName(String theName) {
    		if (_names_to_fb_users == null)
    			apiGetFriends();
    		
    		return _names_to_fb_users.get(theName);
    	}
    	
    	private FacebookUser getUserFromUID(long theUid) {
    		if (_uids_to_fb_users == null)
    			apiGetFriends();
    		
    		return _uids_to_fb_users.get(theUid);
    	}
    };
	
	/**
	 * called once when this service is created.
	 */
	@Override
	protected void onCreate() {
		// read API credentials from XML resource file
		_fb_api_credentials = FacebookApiCredentialsReader.getCredentials(this);
		
		// get auth token: a one-session key 
		// we that sign our communications with 
		getAuthToken();
		
		// read login and password from preferences
		SharedPreferences aPreferences = getSharedPreferences(PREFS_NAME, 0);
		String aLogin = aPreferences.getString("login", "");
		String aPassword = aPreferences.getString("password", "");
		
		if (!aLogin.equals("") && !aPassword.equals("")) {
			// simulate a web browser that logs the user in to facebook
			FacebookMockLoginBrowser.login(_fb_api_credentials.getApiKey(), _fb_auth_token, aLogin, aPassword);
			
			// get a session key for further communication
			getSession();
		}
	}
	
	@Override
	public IBinder onBind(Intent intent) {
		return _binder;
	}

	/** 
	 * get auth token: a one-session key
	 * we that sign our communications with
	 */ 
	private void getAuthToken() {
		HashMap<String, String> aParameters = new HashMap<String, String>();
		aParameters.put("api_key", _fb_api_credentials.getApiKey());
		aParameters.put("method", METHOD_CREATE_TOKEN);
		aParameters.put("v", "1.0");
		aParameters.put("sig", FacebookApiFetcher.getSignature(aParameters, _fb_api_credentials.getSecretKey()));
		
		FacebookData aFbData = FacebookApiFetcher.doRequest(aParameters);
		if (aFbData.auth_token != null)
			_fb_auth_token = aFbData.auth_token;
	}
	
	/**
	 * get a session key for further communication
	 */
	private void getSession() {	  
		HashMap<String, String> aParameters = new HashMap<String, String>();
		aParameters.put("api_key", _fb_api_credentials.getApiKey());
		aParameters.put("auth_token", _fb_auth_token);
		aParameters.put("method", METHOD_GET_SESSION);
		aParameters.put("v", "1.0");
		aParameters.put("sig", FacebookApiFetcher.getSignature(aParameters, _fb_api_credentials.getSecretKey()));

		FacebookData aFbData = FacebookApiFetcher.doRequest(aParameters);
		if (aFbData.session != null)
			_fb_session = aFbData.session;
	}
		
	/**
	 * get a list of friends by their uid and name, and populate a Map with it
	 */
	private void apiGetFriends() {
		_names_to_fb_users = new HashMap<String, FacebookUser>();
		_uids_to_fb_users = new HashMap<Long, FacebookUser>();
		
		String aQuery = String.format("SELECT name, uid, pic_square, status FROM user WHERE uid=%s OR uid IN (SELECT uid2 FROM friend WHERE uid1=%s)", _fb_session.uid, _fb_session.uid); 
		FacebookData aData = doFqlRequest(aQuery);
		
		for (FacebookUser aUser : aData.users) {
			_names_to_fb_users.put(aUser.name, aUser);
			_uids_to_fb_users.put(aUser.uid, aUser);
		}
	}
	
	/**
	 * execute a FQL (facebook query language) request
	 */
	private FacebookData doFqlRequest(String theRequest) {
		HashMap<String, String> aParameters = new HashMap<String, String>();
		aParameters.put("api_key", _fb_api_credentials.getApiKey());
		aParameters.put("session_key", _fb_session.session_key);
		aParameters.put("method", METHOD_FQL);
		aParameters.put("query", theRequest);

		// incremented call id.. use current timestamp with milliseconds
		aParameters.put("call_id", Long.toString(System.currentTimeMillis()));
		aParameters.put("v", "1.0");
		aParameters.put("sig", FacebookApiFetcher.getSignature(aParameters, _fb_session.secret));
		
		FacebookData aFbData = FacebookApiFetcher.doRequest(aParameters);
		return aFbData;
	}
}
